package xxl.signals.base.natives 
{
	import flash.events.Event;
	import flash.events.IEventDispatcher;
	import flash.utils.Dictionary;
	import xxl.signals.base.Slot;
	import xxl.signals.core.ISlot;
	import xxl.signals.core.natives.INativeDispatcher;
	import xxl.signals.util.SignalUtil;
	
	/**
	 * 
	 * @author XXL
	 */
	public class NativeSignal implements INativeDispatcher 
	{
		protected var _target:IEventDispatcher;
		protected var _eventType:String;
		protected var _eventClass:Class;
		protected var _valueClasses:Array;
		protected var _useCapture:Boolean;
		protected var slots:Vector.<ISlot> = new Vector.<ISlot>();
		protected var slotCache:Dictionary = new Dictionary();
		
		public function NativeSignal(target:IEventDispatcher = null, eventType:String = "", eventClass:Class = null, useCapture:Boolean = false) 
		{
			this.target = target;
			_eventType = eventType;
			_eventClass = eventClass || Event;
			_valueClasses = [_eventClass];
			_useCapture = useCapture;
		}
		
		protected function registerListenerWithPriority(listener:Function, once:Boolean = false, priority:int = 0):ISlot
		{
			if (!target)
			{
				throw new ArgumentError("target不能为空");
			}
			if (SignalUtil.RegistrationPossible(slotCache, listener, once))
			{
				const newSlot:ISlot = new Slot(listener, this, once, priority);
				slots.push(newSlot);
				slotCache[listener] = newSlot;
				_target.addEventListener(_eventType, newSlot.execute1, _useCapture, priority);
				return newSlot;
			}
			return slotCache[listener];
		}
		
		/* INTERFACE xxl.signals.core.natives.INativeDispatcher */
		
		public function get eventType():String 
		{
			return _eventType;
		}
		
		public function get eventClass():Class 
		{
			return _eventClass;
		}
		
		public function get target():IEventDispatcher 
		{
			return _target;
		}
		
		public function set target(value:IEventDispatcher):void 
		{
			if (value == _target)
			{
				return;
			}
			if (_target)
			{
				removeAll();
			}
			_target = value;
		}
		
		public function dispatchEvent(event:Event):Boolean 
		{
			if (!target)
			{
				throw new ArgumentError("target不能为空");
			}
			if (!event)
			{
				throw new ArgumentError("event不能为空");
			}
			if (!(event is eventClass))
			{
				throw new ArgumentError("event " + event + " 不是 " + eventClass);
			}
			if (event.type != eventType)
			{
				throw new ArgumentError("event type " + eventType + " 不是 " + event.type);
			}
			return target.dispatchEvent(event);
		}
		
		public function addWithPriority(listener:Function, priority:int = 0):ISlot 
		{
			return registerListenerWithPriority(listener, false, priority);
		}
		
		public function addOnceWithPriority(listener:Function, priority:int = 0):ISlot 
		{
			return registerListenerWithPriority(listener, true, priority);
		}
		
		public function add(listener:Function):ISlot 
		{
			return registerListenerWithPriority(listener);
		}
		
		public function get valueClasses():Array 
		{
			return _valueClasses;
		}
		
		public function set valueClasses(value:Array):void 
		{
			var valueClass:Class = null;
			if (value && value.length > 0)
			{
				valueClass = value[0] as Class;
			}
			_eventClass = valueClass || Event;
			_valueClasses = [_eventClass];
		}
		
		public function get numListeners():int 
		{
			return slots.length;
		}
		
		public function addOnce(listener:Function):ISlot 
		{
			return registerListenerWithPriority(listener,true);
		}
		
		public function dispatch(...valueObjects):void 
		{
			if (null == valueObjects)
			{
				throw new ArgumentError("缺少event");
			}
			if (valueObjects.length != 1)
			{
				throw new ArgumentError("参数只能为一个event");
			}
			dispatchEvent(valueObjects[0] as Event);
		}
		
		public function remove(listener:Function):ISlot 
		{
			const slot:ISlot = slotCache[listener];
			if (!slot)
			{
				return null;
			}
			SignalUtil.RemoveSlotFromVector(slots, listener);
			delete slotCache[listener];
			target.removeEventListener(eventType, slot.execute1, _useCapture);
			return slot;
		}
		
		public function removeAll():void 
		{
			for (var i:int = slots.length; i--; )
			{
				target.removeEventListener(eventType, slots[i].execute1, _useCapture);
			}
			slots.length = 0;
			slotCache = new Dictionary();
		}
		
		public function get useCapture():Boolean 
		{
			return _useCapture;
		}
		
	}

}